#![allow(warnings)]
use crate::interpreter::LINE;

use super::{Slot, Object};
use std::fmt;

// 操作数栈（Operand Stack）也常被称为操作栈，它是一个后入先出（Last In First Out，LIFO）栈。
// #[derive(Debug)]
pub struct OperandStack {
    size: usize,
    slots: Vec<Slot>,
}

impl OperandStack {
    pub fn new_operand_stack(max_stack: usize) -> OperandStack {
        OperandStack { 
            size: 0, 
            slots: (0..max_stack).map(|_| Slot::default()).collect()
        }
    }

    pub fn push_int(&mut self, val: i32) {
        self.slots[self.size].set_num(val);
        self.size += 1;
    }

    pub fn pop_int(&mut self) -> i32 {
        self.size -= 1;
        self.slots[self.size].take_num()
    }

    pub fn push_float(&mut self, val: f32) {
        let bytes = val.to_be_bytes();
        self.slots[self.size].set_num(i32::from_be_bytes(bytes));
        self.size += 1;
    }

    pub fn pop_float(&mut self) -> f32 {
        self.size -= 1;
        let bytes = self.slots[self.size].take_num().to_be_bytes();
        f32::from_be_bytes(bytes)
    }

    pub fn push_long(&mut self, val: i64) {
        self.slots[self.size].set_num((val >> 32) as i32);
        self.slots[self.size + 1].set_num(val as i32);
        self.size += 2;
    }

    pub fn pop_long(&mut self) -> i64 {
        self.size -= 2;
        let high = self.slots[self.size].take_num() as i64;
        let low =  self.slots[self.size + 1].take_num() as i64;
        (high << 32) | (low & 0xffffffff)
    }

    pub fn push_double(&mut self, val: f64) {
        let bytes = val.to_be_bytes();
        self.push_long(i64::from_be_bytes(bytes));
    }

    pub fn pop_double(&mut self) -> f64 {
        let bytes = self.pop_long().to_be_bytes();
        f64::from_be_bytes(bytes)
    }

    pub fn push_ref(&mut self, _ref: Option<*mut Object>) {
        self.slots[self.size].set_ref(_ref);
        self.size += 1;
    }

    pub fn pop_ref(&mut self) -> Option<*mut Object> {
        self.size -= 1;
        self.slots[self.size].take_ref()
    }

    pub fn get_ref_from_top(&self, n: usize) -> Option<*mut Object> {
        // match &self.slots[self.size - 1 - n] {
        //     Some(r) => r.get_ref(),
        //     None => None
        // }
        self.slots[self.size - 1 - n].get_ref()
    }

    pub fn push_slot(&mut self, slot: Slot) {
        self.slots[self.size] = slot;
        self.size += 1;
    }

    pub fn pop_slot(&mut self) -> Slot {
        self.size -= 1;
        self.slots[self.size].take()
    }

    pub fn push_boolean(&mut self, val: bool) {
        if val {
            self.push_int(1);
        } else {
            self.push_int(0);
        }
    }

    pub fn pop_boolean(&mut self) -> bool {
        self.pop_int() == 1
    }

    pub fn clear(&mut self) {
        self.size = 0;
        self.slots.iter_mut().for_each(|slot| slot.set_null());
    }

}

impl fmt::Display for OperandStack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let string:Vec<String> = self.slots.iter()
            .map(|slot| format!("{}", slot))
            .collect();
        write!(f, "OperandStack: max_size: {}, size: {}, slots: [{}]", self.slots.len(), self.size, string.join(","))
    }
}